// 2024/7/29: 首个版本
// 2024/8/15: 支持GetGPUDecoderTestResults
// 2024/8/19: 修正转换函数
// 2024/8/21: 修正protobuf解析
// 2024/8/23: caller可为controller的函数增加controller参数输入
// 2024/9/26: 支持CallNativeFunction和MonitoringFlag相关函数
// 2024/10/10: RequestSubscriber增加checkOnly参数
// 2024/12/5: 实现GetDataReferenceInfo, Add/RemoveBusMessage/PointCloudReference
// 2024/12/25: 增加XXXReferenceChanged事件
// 2025/1/13: 支持GetGraphType
// 2025/5/22: 支持GenerateBusMessage

using System;
using System.Collections.Generic;
using System.Text;
using AgencyTypes;
using ASEva;
using Google.Protobuf;

namespace RemoteConsole
{
    enum OutputType
    {
        Invalid,
        Required,
        Optional,
    }

    class NumberChannelRefChangedEventArgs : EventArgs
    {
        public int Channel { get; set; } // 0~
        public bool Add { get; set; }
    }

    class StringChannelRefChangedEventArgs : EventArgs
    {
        public String Channel { get; set; }
        public bool Add { get; set; }
    }

    class AgencyFunction
    {
        public AgencyFunction(object caller)
        {
            funcTable["AddBusMessageReference"] = (input) =>
            {
                var channel = bytesToInt(input, 0);
                Agency.AddBusMessageReference(channel);
                BusMessageReferenceChanged?.Invoke(this, new NumberChannelRefChangedEventArgs{ Channel = channel - 1, Add = true });
                return (OutputType.Required, null);
            };
            funcTable["AddDataLayer"] = (input) =>
            {
                Agency.AddDataLayer(bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["AddPointCloudReference"] = (input) =>
            {
                var channel = bytesToInt(input, -1);
                Agency.AddPointCloudReference(channel);
                PointCloudReferenceChanged?.Invoke(this, new NumberChannelRefChangedEventArgs{ Channel = channel, Add = true });
                return (OutputType.Required, null);
            };
            funcTable["AddSignalReference"] = (input) =>
            {
                var channel = bytesToString(input, null);
                Agency.AddSignalReference(channel);
                SignalReferenceChanged?.Invoke(this, new StringChannelRefChangedEventArgs{ Channel = channel, Add = true });
                return (OutputType.Required, null);
            };
            funcTable["CallNativeFunction"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = CallNativeFunctionInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Optional, Agency.CallNativeFunction(caller, args.Item1, args.Item2, args.Item3));
            };
            funcTable["ConvertTimeIntoSession"] = (input) =>
            {
                return (OutputType.Required, Agency.ConvertTimeIntoSession(bytesToDouble(input, 0))?.ToPB().ToByteArray());
            };
            funcTable["DeleteDataLayer"] = (input) =>
            {
                Agency.DeleteDataLayer(bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["DequeueDataFromNative"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Optional, Agency.DequeueDataFromNative(caller, args.Item1, args.Item2)?.ToPB().ToByteArray());
            };
            funcTable["DisableAllConfigs"] = (input) =>
            {
                Agency.DisableAllConfigs();
                return (OutputType.Required, null);
            };
            funcTable["DisableAllPlugins"] = (input) =>
            {
                Agency.DisableAllPlugins();
                return (OutputType.Required, null);
            };
            funcTable["DisableModule"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                Agency.DisableModule(args.Item1 ?? caller, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["DisablePlugin"] = (input) =>
            {
                Agency.DisablePlugin(bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["EnablePlugin"] = (input) =>
            {
                Agency.EnablePlugin(bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["EnqueueDataToNative"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = EnqueueDataToNativeInput.Parser.ParseFrom(input).ToCommon();
                Agency.EnqueueDataToNative(caller, args.Item1, args.Item2, args.Item3);
                return (OutputType.Required, null);
            };
            funcTable["GenerateBusMessage"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GenerateBusMessageInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, Agency.GenerateBusMessage(args.Item1, args.Item2, args.Item3, args.Item4)?.ToPB().ToByteArray());
            };
            funcTable["GetAllChannelGuestSyncKeys"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAllChannelGuestSyncKeys()?.ToPB().ToByteArray());
            };
            funcTable["GetAllChannelMonitoringKeys"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAllChannelMonitoringKeys()?.ToPB().ToByteArray());
            };
            funcTable["GetAllChannelServerSyncMonitoringKeys"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAllChannelServerSyncMonitoringKeys()?.ToPB().ToByteArray());
            };
            funcTable["GetAllDeviceStatus"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAllDeviceStatus()?.ToPB().ToByteArray());
            };
            funcTable["GetAllRawChannelDelayConfigs"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAllRawChannelDelayConfigs()?.ToPB().ToByteArray());
            };
            funcTable["GetAppGUI"] = (input) =>
            {
                return (OutputType.Required, enumToBytes(Agency.GetAppGUI()));
            };
            funcTable["GetAppID"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetAppID()));
            };
            funcTable["GetAppLanguage"] = (input) =>
            {
                return (OutputType.Required, enumToBytes(Agency.GetAppLanguage()));
            };
            funcTable["GetAppMode"] = (input) =>
            {
                return (OutputType.Required, enumToBytes(Agency.GetAppMode()));
            };
            funcTable["GetAppStatus"] = (input) =>
            {
                return (OutputType.Required, enumToBytes(Agency.GetAppStatus()));
            };
            funcTable["GetAudioChannelDelayConfig"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetAudioChannelDelayConfig()));
            };
            funcTable["GetAudioChannelStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var arg = OptionalUInt.Parser.ParseFrom(input).ToCommon();
                List<double> item2 = null, item3 = null;
                bool item1 = Agency.GetAudioChannelStatus(arg, out item2, out item3);
                return (OutputType.Required, (item1, item2?.ToArray(), item3?.ToArray()).ToPB().ToByteArray());
            };
            funcTable["GetAudioDrivers"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAudioDrivers()?.ToPB().ToByteArray());
            };
            funcTable["GetAudioRecordDevices"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAudioRecordDevices(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetAudioReplayDevices"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAudioReplayDevices(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetAvailableBusChannels"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAvailableBusChannels()?.ToPB().ToByteArray());
            };
            funcTable["GetAvailableRawChannels"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAvailableRawChannels()?.ToPB().ToByteArray());
            };
            funcTable["GetAvailableSampleChannels"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAvailableSampleChannels()?.ToPB().ToByteArray());
            };
            funcTable["GetAvailableVideoChannels"] = (input) =>
            {
                return (OutputType.Required, Agency.GetAvailableVideoChannels()?.ToPB().ToByteArray());
            };
            funcTable["GetBufferRange"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBufferRange().ToPB().ToByteArray());
            };
            funcTable["GetBusChannelDelayConfig"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetBusChannelDelayConfig(bytesToInt(input, 0))));
            };
            funcTable["GetBusChannelsInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBusChannelsInfo(bytesToSession(input))?.ToPB().ToByteArray());
            };
            funcTable["GetBusChannelStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetChannelStatusInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.GetBusChannelStatus(args.Item1, args.Item2)));
            };
            funcTable["GetBusDevices"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBusDevices()?.ToPB().ToByteArray());
            };
            funcTable["GetBusMessageFPS"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = IntAndUInt.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, floatToBytes(Agency.GetBusMessageFPS(args.Item1, args.Item2)));
            };
            funcTable["GetBusMessageInfoByLocalID"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = IntAndUInt.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, Agency.GetBusMessageInfoByLocalID(args.Item1, args.Item2)?.ToPB().ToByteArray());
            };
            funcTable["GetBusMessageInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBusMessageInfo(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetBusPayloadPercentage"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBusPayloadPercentage(bytesToInt(input, 0)).ToPB().ToByteArray());
            };
            funcTable["GetBusProtocolFileChannel"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBusProtocolFileChannel(bytesToString(input, null)).ToPB().ToByteArray());
            };
            funcTable["GetBusProtocolFileIDList"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBusProtocolFileIDList()?.ToPB().ToByteArray());
            };
            funcTable["GetBusProtocolFileState"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var arg = AgencyTypes.BusProtocolFileID.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, enumToBytes(Agency.GetBusProtocolFileState(arg)));
            };
            funcTable["GetBusSignalInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetBusSignalInfo(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetChannelAliasName"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetChannelAliasName(bytesToString(input, null))));
            };
            funcTable["GetChannelAliasTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetChannelAliasTable()?.ToPB().ToByteArray());
            };
            funcTable["GetChannelGuestSyncFlag"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.GetChannelGuestSyncFlag(bytesToString(input, null))));
            };
            funcTable["GetChannelLatestTimestamps"] = (input) =>
            {
                return (OutputType.Optional, Agency.GetChannelLatestTimestamps(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetChannelMonitoringFlag"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.GetChannelMonitoringFlag(bytesToString(input, null))));
            };
            funcTable["GetChannelServerSyncMonitoringFlag"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.GetChannelServerSyncMonitoringFlag(bytesToString(input, null))));
            };
            funcTable["GetChannelStatusTable"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var arg = OptionalUInt.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, Agency.GetChannelStatusTable(arg)?.ToPB().ToByteArray());
            };
            funcTable["GetChannelSyncTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetChannelSyncTable()?.ToPB().ToByteArray());
            };
            funcTable["GetChildDeviceStatus"] = (input) =>
            {
                return (OutputType.Required, Agency.GetChildDeviceStatus(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetConsoleClassInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetConsoleClassInfo(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetConsoleClassTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetConsoleClassTable()?.ToPB().ToByteArray());  
            };
            funcTable["GetConsoleRelatedModulesConfigStatus"] = (input) =>
            {
                ConfigStatus[] item2 = null;
                var item1 = Agency.GetConsoleRelatedModulesConfigStatus(bytesToString(input, null), out item2);
                return (OutputType.Required, (item1, item2).ToPB().ToByteArray());
            };
            funcTable["GetCPUTick"] = (input) =>
            {
                return (OutputType.Required, ulongToBytes(Agency.GetCPUTick()));
            };
            funcTable["GetCPUTicksPerSecond"] = (input) =>
            {
                return (OutputType.Required, ulongToBytes(Agency.GetCPUTicksPerSecond()));
            };
            funcTable["GetCPUTime"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetCPUTime()));
            };
            funcTable["GetCPUTimeModel"] = (input) =>
            {
                return (OutputType.Required, Agency.GetCPUTimeModel(bytesToSession(input))?.ToPB().ToByteArray());
            };
            funcTable["GetCurrentDataGeneration"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetCurrentDataGeneration()));
            };
            funcTable["GetCurrentDataLayer"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetCurrentDataLayer()));
            };
            funcTable["GetCurrentOnlineSession"] = (input) =>
            {
                return (OutputType.Required, optionalSessionToBytes(Agency.GetCurrentOnlineSession()));
            };
            funcTable["GetCurrentSessionGUID"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetCurrentSessionGUID()));
            };
            funcTable["GetDataLayers"] = (input) =>
            {
                return (OutputType.Required, Agency.GetDataLayers()?.ToPB().ToByteArray());
            };
            funcTable["GetDataReferenceInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetDataReferenceInfo()?.ToPB().ToByteArray());
            };
            funcTable["GetDeviceClassTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetDeviceClassTable()?.ToPB().ToByteArray());
            };
            funcTable["GetDeviceStatus"] = (input) =>
            {
                return (OutputType.Required, enumToBytes(Agency.GetDeviceStatus(bytesToString(input, null))));
            };
            funcTable["GetDialogRelatedModulesConfigStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                ConfigStatus[] item2;
                var item1 = Agency.GetDialogRelatedModulesConfigStatus(args.Item1, args.Item2, out item2);
                return (OutputType.Required, (item1, item2).ToPB().ToByteArray());
            };
            funcTable["GetEventHandles"] = (input) =>
            {
                var handles = Agency.GetEventHandles();
                var guids = new List<String>();
                foreach (var handle in handles ?? [])
                {
                    if (handle is String) guids.Add(handle as String);
                }
                return (OutputType.Optional, guids.ToArray().ToPB().ToByteArray());
            };
            funcTable["GetEventInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetEventInfo(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetEventTypeNames"] = (input) =>
            {
                return (OutputType.Required, Agency.GetEventTypeNames()?.ToPB().ToByteArray());
            };
            funcTable["GetFilteredSessionList"] = (input) =>
            {
                return (OutputType.Required, Agency.GetFilteredSessionList()?.ToPB().ToByteArray());
            };
            funcTable["GetFilteredSessionListTotalLength"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetFilteredSessionListTotalLength()));
            };
            funcTable["GetFinishedSessions"] = (input) =>
            {
                return (OutputType.Required, Agency.GetFinishedSessions(bytesToString(input, null))?.ToPB().ToByteArray());  
            };
            funcTable["GetFrameworkThirdPartyNotices"] = (input) =>
            {
                return (OutputType.Optional, Agency.GetFrameworkThirdPartyNotices()?.ToPB().ToByteArray());
            };
            funcTable["GetGenerationList"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGenerationList()?.ToPB().ToByteArray());
            };
            funcTable["GetGenerationProcessStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                var arg1 = bytesToSession(Encoding.UTF8.GetBytes(args.Item1));
                return (OutputType.Required, stringToBytes(Agency.GetGenerationProcessStatus(arg1, args.Item2)?.ToString()));
            };
            funcTable["GetGenerationSessions"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGenerationSessions(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetGlobalParameter"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Optional, stringToBytes(Agency.GetGlobalParameter(args.Item1, args.Item2)));
            };
            funcTable["GetGlobalParameterKeys"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGlobalParameterKeys()?.ToPB().ToByteArray());
            };
            funcTable["GetGlobalVariable"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Optional, stringToBytes(Agency.GetGlobalVariable(args.Item1, args.Item2)));
            };
            funcTable["GetGlobalVariableKeys"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGlobalVariableKeys()?.ToPB().ToByteArray());
            };
            funcTable["GetGNSSPosixTimeModel"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGNSSPosixTimeModel(bytesToSession(input))?.ToPB().ToByteArray());
            };
            funcTable["GetGPUDecoderTestResults"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGPUDecoderTestResults()?.ToPB().ToByteArray());
            };
            funcTable["GetGraphData"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetGraphDataInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Optional, Agency.GetGraphData(args.Item1, args.Item2)?.ToPB().ToByteArray());
            };
            funcTable["GetGraphicCardInfos"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGraphicCardInfos()?.ToPB().ToByteArray());
            };
            funcTable["GetGraphIDList"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGraphIDList()?.ToPB().ToByteArray());
            };
            funcTable["GetGraphIDWithTitle"] = (input) =>
            {
                return (OutputType.Required, Agency.GetGraphIDWithTitle(bytesToString(input, null)).ToPB().ToByteArray());
            };
            funcTable["GetGraphTitle"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetGraphTitle(bytesToInt(input, 0))));
            };
            funcTable["GetGraphType"] = (input) =>
            {
                return (OutputType.Required, enumToBytes(Agency.GetGraphType(bytesToInt(input, 0))));
            };
            funcTable["GetHostPosixTimeModel"] = (input) =>
            {
                return (OutputType.Required, Agency.GetHostPosixTimeModel(bytesToSession(input))?.ToPB().ToByteArray());
            };
            funcTable["GetInterestTarget"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetInterestTarget()));
            };
            funcTable["GetInterestTime"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetInterestTime()));
            };
            funcTable["GetInterestTimestamp"] = (input) =>
            {
                var output = Agency.GetInterestTimestamp()?.ToString("yyyy-MM-dd-HH-mm-ss-fff");
                return (OutputType.Required, stringToBytes(output));
            };
            funcTable["GetInternetNTPTime"] = (input) =>
            {
                var output = Agency.GetInternetNTPTime()?.ToString("yyyy-MM-dd-HH-mm-ss-fff");
                return (OutputType.Required, stringToBytes(output));
            };
            funcTable["GetLicensedFunctionIndices"] = (input) =>
            {
                return (OutputType.Required, Agency.GetLicensedFunctionIndices()?.ToPB().ToByteArray());
            };
            funcTable["GetLicenseInfo"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetLicenseInfo()));
            };
            funcTable["GetLocalDateTime"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetDateTimeInput.Parser.ParseFrom(input).ToCommon();
                var output = Agency.GetLocalDateTime(args.Item1, args.Item2, args.Item3)?.ToString("yyyy-MM-dd-HH-mm-ss-fff");
                return (OutputType.Required, stringToBytes(output));
            };
            funcTable["GetLogMessages"] = (input) =>
            {
                return (OutputType.Optional, Agency.GetLogMessages()?.ToPB().ToByteArray());
            };
            funcTable["GetManualTriggerNames"] = (input) =>
            {
                return (OutputType.Required, Agency.GetManualTriggerNames()?.ToPB().ToByteArray());
            };
            funcTable["GetManualTriggerName"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetManualTriggerName(bytesToInt(input, -1))));
            };
            funcTable["GetModuleChildConfigStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, Agency.GetModuleChildConfigStatus(args.Item1 ?? caller, args.Item2)?.ToPB().ToByteArray());
            };
            funcTable["GetModuleConfig"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, stringToBytes(Agency.GetModuleConfig(args.Item1 ?? caller, args.Item2)));
            };
            funcTable["GetModuleConfigStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                String item2 = null;
                var item1 = Agency.GetModuleConfigStatus(args.Item1 ?? caller, args.Item2, out item2);
                return (OutputType.Required, (item1, item2).ToPB().ToByteArray());
            };
            funcTable["GetModuleDetails"] = (input) =>
            {
                return (OutputType.Required, Agency.GetModuleDetails(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetNativeClassTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetNativeClassTable()?.ToPB().ToByteArray());
            };
            funcTable["GetNativePluginVersions"] = (input) =>
            {
                return (OutputType.Required, Agency.GetNativePluginVersions(bytesToEnum(input, NativeLibraryType.Native))?.ToPB().ToByteArray());
            };
            funcTable["GetPluginGuestSyncTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetPluginGuestSyncTable()?.ToPB().ToByteArray());
            };
            funcTable["GetPluginPackIDList"] = (input) =>
            {
                return (OutputType.Required, Agency.GetPluginPackIDList()?.ToPB().ToByteArray());
            };
            funcTable["GetPluginPackInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetPluginPackInfo(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetPluginThirdPartyNotices"] = (input) =>
            {
                return (OutputType.Optional, Agency.GetPluginThirdPartyNotices()?.ToPB().ToByteArray());
            };
            funcTable["GetPreviewJpeg"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetPreviewJpegInput.Parser.ParseFrom(input).ToCommon();
                ASEva.Timestamp? item2 = null;
                ASEva.Samples.CameraInfo item3 = null;
                var item1 = Agency.GetPreviewJpeg(args.Item1, args.Item2, args.Item3, out item2, out item3);
                return (OutputType.Optional, (item1, item2, item3).ToPB().ToByteArray());
            };
            funcTable["GetProcessorClassTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetProcessorClassTable()?.ToPB().ToByteArray());
            };
            funcTable["GetRawChannelDelayConfig"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetRawChannelDelayConfig(bytesToString(input, null))));
            };
            funcTable["GetRawChannelStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndOptionalUInt.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.GetRawChannelStatus(args.Item1, args.Item2)));
            };
            funcTable["GetSampleChannelStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndOptionalUInt.Parser.ParseFrom(input).ToCommon();
                List<double> item2 = null, item3 = null;
                var item1 = Agency.GetSampleChannelStatus(args.Item1, args.Item2, out item2, out item3);
                return (OutputType.Required, (item1, item2?.ToArray(), item3?.ToArray()).ToPB().ToByteArray());
            };
            funcTable["GetSampleTitle"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSampleTitle(bytesToString(input, null))?.ToArray().ToPB().ToByteArray());
            };
            funcTable["GetSceneIDList"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSceneIDList()?.ToPB().ToByteArray());
            };
            funcTable["GetSceneTitleTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSceneTitleTable()?.ToPB().ToByteArray());
            };
            funcTable["GetSessionComment"] = (input) =>
            {
                return (OutputType.Optional, stringToBytes(Agency.GetSessionComment(bytesToSession(input))));
            };
            funcTable["GetSessionFilterTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSessionFilterTable()?.ToPB().ToByteArray());
            };
            funcTable["GetSessionFolderName"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetSessionFolderName(bytesToSession(input))));
            };
            funcTable["GetSessionGenerations"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSessionGenerations(bytesToSession(input))?.ToPB().ToByteArray());
            };
            funcTable["GetSessionHostSync"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.GetSessionHostSync(bytesToSession(input))));
            };
            funcTable["GetSessionLayer"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetSessionLayer(bytesToSession(input))));
            };
            funcTable["GetSessionLength"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSessionLength(bytesToSession(input)).ToPB().ToByteArray());
            };
            funcTable["GetSessionList"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSessionList()?.ToPB().ToByteArray());
            };
            funcTable["GetSessionListTotalLength"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetSessionListTotalLength()));
            };
            funcTable["GetSessionProperties"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSessionProperties(bytesToSession(input))?.ToPB().ToByteArray());
            };
            funcTable["GetSessionSearchKey"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetSessionSearchKey()));
            };
            funcTable["GetSessionTimeline"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSessionTimeline(bytesToSession(input)).ToPB().ToByteArray());
            };
            funcTable["GetSignalName"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, stringToBytes(Agency.GetSignalName(args.Item1, args.Item2)));
            };
            funcTable["GetSignalNamesOfBusMessage"] = (input) =>
            {
                return (OutputType.Required, Agency.GetSignalNamesOfBusMessage(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetSignalTree"] = (input) =>
            {
                return (OutputType.Optional, Agency.GetSignalTree()?.ToPB().ToByteArray());
            };
            funcTable["GetSystemStatus"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetSystemStatus(bytesToEnum(input, SystemStatus.CPUUsageRatio/* 1 */))));
            };
            funcTable["GetSystemStatusDetails"] = (input) =>
            {
                return (OutputType.Required, stringToBytes(Agency.GetSystemStatusDetails(bytesToEnum(input, SystemStatus.CPUUsageRatio/* 1 */))));
            };
            funcTable["GetTaskClassInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetTaskClassInfo(bytesToString(input, null))?.ToPB().ToByteArray());
            };
            funcTable["GetTaskClassTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetTaskClassTable()?.ToPB().ToByteArray());  
            };
            funcTable["GetUTCDateTime"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetDateTimeInput.Parser.ParseFrom(input).ToCommon();
                var output = Agency.GetUTCDateTime(args.Item1, args.Item2, args.Item3)?.ToString("yyyy-MM-dd-HH-mm-ss-fff");
                return (OutputType.Required, stringToBytes(output));
            };
            funcTable["GetVersionTable"] = (input) =>
            {
                return (OutputType.Required, Agency.GetVersionTable()?.ToPB().ToByteArray());
            };
            funcTable["GetVideoChannelDelayConfig"] = (input) =>
            {
                return (OutputType.Required, doubleToBytes(Agency.GetVideoChannelDelayConfig(bytesToInt(input, -1))));
            };
            funcTable["GetVideoChannelsInfo"] = (input) =>
            {
                return (OutputType.Required, Agency.GetVideoChannelsInfo(bytesToSession(input))?.ToPB().ToByteArray());
            };
            funcTable["GetVideoChannelStatus"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetChannelStatusInput.Parser.ParseFrom(input).ToCommon();
                List<double> item2 = null, item3 = null;
                var item1 = Agency.GetVideoChannelStatus(args.Item1, args.Item2, out item2, out item3);
                return (OutputType.Required, (item1, item2?.ToArray(), item3?.ToArray()).ToPB().ToByteArray());
            };
            funcTable["GetVideoDevices"] = (input) =>
            {
                return (OutputType.Required, Agency.GetVideoDevices()?.ToPB().ToByteArray());
            };
            funcTable["GetVideoFrameImage"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetVideoFrameImageInput.Parser.ParseFrom(input).ToCommon();
                var arg4 = bytesToEnum(Encoding.UTF8.GetBytes(args.Item4), VideoFrameGetMode.Preview);
                ASEva.Timestamp? item2 = null;
                ASEva.Samples.CameraInfo item3 = null;
                var item1 = Agency.GetVideoFrameImage(args.Item1, args.Item2, args.Item3, arg4, args.Item5, args.Item6, out item2, out item3);
                return (OutputType.Optional, (item1, item2, item3).ToPB().ToByteArray());
            };
            funcTable["GetVideoFrameThumbnail"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetVideoFrameThumbnailInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Optional, Agency.GetVideoFrameThumbnail(args.Item1, args.Item2, args.Item3, args.Item4)?.ToPB().ToByteArray());
            };
            funcTable["GetVideoRawSize"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = IntAndDouble.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, Agency.GetVideoRawSize(args.Item1, args.Item2)?.ToPB().ToByteArray());
            };
            funcTable["GetVideoSpecialType"] = (input) =>
            {
                return (OutputType.Required, enumToBytes(Agency.GetVideoSpecialType(bytesToInt(input, -1))));
            };
            funcTable["IsBusMessageBound"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.IsBusMessageBound(bytesToString(input, null))));
            };
            funcTable["IsFileOutputEnabled"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.IsFileOutputEnabled()));
            };
            funcTable["IsInputChannelAvailable"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.IsInputChannelAvailable(bytesToString(input, null))));
            };
            funcTable["IsInternetConnected"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.IsInternetConnected()));
            };
            funcTable["IsMessageValid"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.IsMessageValid(args.Item1, args.Item2)));
            };
            funcTable["IsPRCWebPreferred"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.IsPRCWebPreferred()));
            };
            funcTable["IsReady"] = (input) =>
            {
                String item1 = null;
                var item2 = Agency.IsReady(out item1);
                return (OutputType.Required, (item1, item2).ToPB().ToByteArray());
            };
            funcTable["IsSampleChannelConflict"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.IsSampleChannelConflict(bytesToString(input, null))));
            };
            funcTable["IsSignalValid"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.IsSignalValid(args.Item1, args.Item2)));
            };
            funcTable["IsVideoDataAvailable"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = GetChannelStatusInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.IsVideoDataAvailable(args.Item1, args.Item2)));
            };
            funcTable["ParseBusMessage"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var arg = BusMessageSample.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, Agency.ParseBusMessage(arg)?.ToPB().ToByteArray());
            };
            funcTable["PublishData"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = PublishDataInput.Parser.ParseFrom(input).ToCommon();
                Agency.PublishData(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["RefreshGenerations"] = (input) =>
            {
                Agency.RefreshGenerations();
                return (OutputType.Required, null);
            };
            funcTable["RefreshSessions"] = (input) =>
            {
                Agency.RefreshSessions();
                return (OutputType.Required, null);
            };
            funcTable["RemoveBusMessageReference"] = (input) =>
            {
                var channel = bytesToInt(input, 0);
                Agency.RemoveBusMessageReference(channel);
                BusMessageReferenceChanged?.Invoke(this, new NumberChannelRefChangedEventArgs{ Channel = channel - 1, Add = false });
                return (OutputType.Required, null);
            };
            funcTable["RemoveEvent"] = (input) =>
            {
                Agency.RemoveEvent(bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["RemoveGeneration"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                var arg1 = SessionIdentifier.FromString(args.Item1);
                Agency.RemoveGeneration(arg1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["RemovePointCloudReference"] = (input) =>
            {
                var channel = bytesToInt(input, -1);
                Agency.RemovePointCloudReference(channel);
                PointCloudReferenceChanged?.Invoke(this, new NumberChannelRefChangedEventArgs{ Channel = channel, Add = false });
                return (OutputType.Required, null);
            };
            funcTable["RemoveSession"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                var arg1 = SessionIdentifier.FromString(args.Item1);
                return (OutputType.Required, boolToBytes(Agency.RemoveSession(arg1, args.Item2)));
            };
            funcTable["RemoveSignalReference"] = (input) =>
            {
                var channel = bytesToString(input, null);
                Agency.RemoveSignalReference(channel);
                SignalReferenceChanged?.Invoke(this, new StringChannelRefChangedEventArgs{ Channel = channel, Add = false });
                return (OutputType.Required, null);
            };
            funcTable["ResetGPUDecoderTestResults"] = (input) =>
            {
                Agency.ResetGPUDecoderTestResults();
                return (OutputType.Required, null);
            };
            funcTable["RunConsole"] = (input) =>
            {
                Agency.RunConsole(caller, bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["RunStandaloneTask"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                String item2 = null;
                var item1 = Agency.RunStandaloneTask(caller, args.Item1, args.Item2, out item2);
                return (OutputType.Required, (item1.ToString(), item2).ToPB().ToByteArray());
            };
            funcTable["SendBusMessage"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var arg = AgencyTypes.BusMessage.Parser.ParseFrom(input).ToCommon();
                Agency.SendBusMessage(arg);
                return (OutputType.Required, null);
            };
            funcTable["SendBusMessageBound"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndOptionalUInt.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, Agency.SendBusMessageBound(args.Item1, args.Item2));
            };
            funcTable["SendManualTrigger"] = (input) =>
            {
                Agency.SendManualTrigger(bytesToInt(input, -1));
                return (OutputType.Required, null);
            };
            funcTable["SendRawData"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = SendRawDataInput.Parser.ParseFrom(input).ToCommon();
                Agency.SendRawData(args.Item1, args.Item2, args.Item3);
                return (OutputType.Required, null);
            };
            funcTable["SetAudioChannelDelayConfig"] = (input) =>
            {
                Agency.SetAudioChannelDelayConfig(bytesToDouble(input, 0));
                return (OutputType.Required, null);
            };
            funcTable["SetBusChannelDelayConfig"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = IntAndDouble.Parser.ParseFrom(input).ToCommon();
                Agency.SetBusChannelDelayConfig(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetChannelGuestSyncFlag"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                Agency.SetChannelGuestSyncFlag(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetChannelMonitoringFlag"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                Agency.SetChannelMonitoringFlag(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetChannelServerSyncMonitoringFlag"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                Agency.SetChannelServerSyncMonitoringFlag(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetControlFlag"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.SetControlFlag(args.Item1, args.Item2)));
            };
            funcTable["SetCurrentDataLayer"] = (input) =>
            {
                Agency.SetCurrentDataLayer(bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["SetEventComment"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                Agency.SetEventComment(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetGlobalParameter"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                Agency.SetGlobalParameter(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetGlobalVariable"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                Agency.SetGlobalVariable(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetInterestTime"] = (input) =>
            {
                Agency.SetInterestTime(bytesToDouble(input, 0));
                return (OutputType.Required, null);
            };
            funcTable["SetInterestTimestamp"] = (input) =>
            {
                DateTime arg1;
                if (!DateTime.TryParseExact(bytesToString(input, ""), "yyyy-MM-dd-HH-mm-ss-fff", null, System.Globalization.DateTimeStyles.None, out arg1)) return (OutputType.Required, null);
                Agency.SetInterestTimestamp(arg1);
                return (OutputType.Required, null);
            };
            funcTable["SetManualTriggerName"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = IntAndString.Parser.ParseFrom(input).ToCommon();
                Agency.SetManualTriggerName(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetModuleConfig"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = ThreeStrings.Parser.ParseFrom(input).ToCommon();
                Agency.SetModuleConfig(args.Item1 ?? caller, args.Item2, args.Item3);
                return (OutputType.Required, null);
            };
            funcTable["SetRawChannelDelayConfig"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndDouble.Parser.ParseFrom(input).ToCommon();
                Agency.SetRawChannelDelayConfig(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetSessionChecker"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                var arg1 = SessionIdentifier.FromString(args.Item1);
                Agency.SetSessionChecker(arg1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetSessionComment"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoStrings.Parser.ParseFrom(input).ToCommon();
                var arg1 = SessionIdentifier.FromString(args.Item1);
                Agency.SetSessionComment(arg1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetSessionHostSync"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                var arg1 = SessionIdentifier.FromString(args.Item1);
                Agency.SetSessionHostSync(arg1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetSessionProperties"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = SetSessionPropertiesInput.Parser.ParseFrom(input).ToCommon();
                var arg1 = SessionIdentifier.FromString(args.Item1);
                Agency.SetSessionProperties(arg1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["SetSessionSearchKeyword"] = (input) =>
            {
                Agency.SetSessionSearchKeyword(bytesToString(input, null));
                return (OutputType.Required, null);
            };
            funcTable["SetTargetReplaySpeed"] = (input) =>
            {
                Agency.SetTargetReplaySpeed(bytesToDouble(input, 1));
                return (OutputType.Required, null);
            };
            funcTable["SetVideoChannelDelayConfig"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = IntAndDouble.Parser.ParseFrom(input).ToCommon();
                Agency.SetVideoChannelDelayConfig(args.Item1, args.Item2);
                return (OutputType.Required, null);
            };
            funcTable["StartOffline"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StartOfflineOnlineInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.StartOffline(args.Item1, args.Item2, args.Item3)));
            };
            funcTable["StartOnlineWithController"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StringAndBool.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.StartOnlineWithController(args.Item1, args.Item2)));
            };
            funcTable["StartOnline"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StartOfflineOnlineInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.StartOnline(args.Item1, args.Item2, args.Item3)));
            };
            funcTable["StartRemote"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StartRemoteInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.StartRemote(args.Item1, args.Item2, args.Item3, args.Item4)));
            };
            funcTable["StartRemoteWithController"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StartRemoteWithControllerInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.StartRemoteWithController(args.Item1, args.Item2, args.Item3)));
            };
            funcTable["StartReplay"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = StartReplayInput.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.StartReplay(args.Item1, args.Item2, args.Item3)));
            };
            funcTable["StopRunningWithController"] = (input) =>
            {
                return (OutputType.Required, boolToBytes(Agency.StopRunningWithController(bytesToString(input, ""))));
            };
            funcTable["StopRunning"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = TwoBools.Parser.ParseFrom(input).ToCommon();
                return (OutputType.Required, boolToBytes(Agency.StopRunning(args.Item1, args.Item2)));
            };
            funcTable["SubscribeData"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = SubscribeDataInput.Parser.ParseFrom(input).ToCommon();

                var subscriber = Agency.SubscribeData(args.Item1, args.Item2, args.Item3);
                String guid = null;
                if (subscriber != null)
                {
                    guid = Guid.NewGuid().ToString().Replace("-", "");
                    lock (subscribers)
                    {
                        subscribers[guid] = subscriber;
                    }
                }
                return (OutputType.Required, stringToBytes(guid));
            };
            funcTable["SwitchAppMode"] = (input) =>
            {
                if (input == null) return (OutputType.Required, null);
                var args = SwitchAppModeInput.Parser.ParseFrom(input).ToCommon();
                var arg2 = bytesToEnum(Encoding.UTF8.GetBytes(args.Item2), ApplicationMode.Unknown);
                return (OutputType.Required, boolToBytes(Agency.SwitchAppMode(args.Item1, arg2, args.Item3)));
            };
        }

        public (OutputType, byte[]) Call(String function, byte[] input)
        {
            if (funcTable.ContainsKey(function)) return funcTable[function].Invoke(input);
            else return (OutputType.Invalid, null);
        }

        public (bool, byte[][]) RequestSubscriber(String guid, bool closeRequest, bool checkOnly)
        {
            DataSubscriber subscriber = null;
            lock (subscribers)
            {
                if (!subscribers.ContainsKey(guid)) return (true, null);
                else subscriber = subscribers[guid];
            }

            if (closeRequest || subscriber.IsClosed())
            {
                subscriber.Close();
                lock (subscribers)
                {
                    subscribers.Remove(guid);
                }
                return (true, null);
            }

            return (false, checkOnly ? null : subscriber.Dequeue());
        }

        public event EventHandler<NumberChannelRefChangedEventArgs> BusMessageReferenceChanged;
        public event EventHandler<NumberChannelRefChangedEventArgs> PointCloudReferenceChanged;
        public event EventHandler<StringChannelRefChangedEventArgs> SignalReferenceChanged;

        protected byte[] stringToBytes(String str)
        {
            if (str == null || str.Length > MaxStringLength) return [0];
            else 
            {
                var utf8 = Encoding.UTF8.GetBytes(str);
                var bytes = new byte[1 + utf8.Length];
                bytes[0] = 1;
                Array.Copy(utf8, 0, bytes, 1, utf8.Length);
                return bytes;
            }
        }

        protected String bytesToString(byte[] bytes, String defaultVal)
        {
            if (bytes == null || bytes.Length < 1 || bytes[0] == 0 || bytes.Length > MaxStringLength) return defaultVal;
            return Encoding.UTF8.GetString(bytes.AsSpan(1));
        }

        protected byte[] doubleToBytes(double num)
        {
            var bytes = new byte[8];
            BitConverter.TryWriteBytes(bytes.AsSpan(), num);
            return bytes;
        }

        protected double bytesToDouble(byte[] bytes, double defaultVal)
        {
            if (bytes == null || bytes.Length != 8) return defaultVal;
            return BitConverter.ToDouble(bytes);
        }

        protected byte[] intToBytes(int num)
        {
            var bytes = new byte[4];
            BitConverter.TryWriteBytes(bytes.AsSpan(), num);
            return bytes;
        }

        protected int bytesToInt(byte[] bytes, int defaultVal)
        {
            if (bytes == null || bytes.Length != 4) return defaultVal;
            return BitConverter.ToInt32(bytes);
        }

        protected byte[] sessionToBytes(SessionIdentifier session)
        {
            return Encoding.UTF8.GetBytes(session.ToString());
        }

        protected SessionIdentifier bytesToSession(byte[] bytes)
        {
            if (bytes == null || bytes.Length == 0) return new SessionIdentifier(0, 0, 0, 0, 0, 0);
            return SessionIdentifier.FromString(Encoding.UTF8.GetString(bytes));
        }

        protected byte[] optionalSessionToBytes(SessionIdentifier? session)
        {
            if (session == null) return null;
            else return sessionToBytes(session.Value);
        }

        protected SessionIdentifier? bytesToOptionalSession(byte[] bytes)
        {
            if (bytes == null || bytes.Length == 0) return null;
            else
            {
                var session = SessionIdentifier.FromString(Encoding.UTF8.GetString(bytes));
                if (session == new SessionIdentifier(0, 0, 0, 0, 0, 0)) return null;
                else return session;
            }
        }

        protected byte[] boolToBytes(bool boolean)
        {
            return [(byte)(boolean ? 1 : 0)];
        }

        protected bool bytesToBool(byte[] bytes)
        {
            if (bytes == null || bytes.Length != 1) return false;
            return bytes[0] == 1;
        }

        protected byte[] floatToBytes(float num)
        {
            var bytes = new byte[4];
            BitConverter.TryWriteBytes(bytes.AsSpan(), num);
            return bytes;
        }

        protected float bytesToFloat(byte[] bytes, float defaultVal)
        {
            if (bytes == null || bytes.Length != 4) return defaultVal;
            return BitConverter.ToSingle(bytes);
        }

        protected byte[] ulongToBytes(ulong num)
        {
            var bytes = new byte[8];
            BitConverter.TryWriteBytes(bytes.AsSpan(), num);
            return bytes;
        }

        protected ulong bytesToULong(byte[] bytes, ulong defaultVal)
        {
            if (bytes == null || bytes.Length != 8) return defaultVal;
            return BitConverter.ToUInt64(bytes);
        }

        protected byte[] enumToBytes<T>(T val) where T : struct, Enum
        {
            return Encoding.UTF8.GetBytes(val.ToString());
        }

        protected T bytesToEnum<T>(byte[] bytes, T defaultVal) where T : struct, Enum
        {
            if (bytes == null || bytes.Length == 0) return defaultVal;
            T output;
            if (Enum.TryParse(Encoding.UTF8.GetString(bytes), out output)) return output;
            else return defaultVal;
        }

        protected Dictionary<String, Func<byte[], (OutputType, byte[])>> funcTable = new Dictionary<string, Func<byte[], (OutputType, byte[])>>();
        private Dictionary<String, DataSubscriber> subscribers = new Dictionary<string, DataSubscriber>();

        private const int MaxStringLength = 67108864; // 64M
    }
}